#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <3ds.h>

#include <malloc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>

#include "text.h"

#define PATH_EXISTS (0xC82044BE)
FS_archive sdmcArchive;

#define print(...) sprintf(&superStr[strlen(superStr)], __VA_ARGS__); drawFrame()
char superStr[8192];

int countLines(char* str)
{
	if(!str)return 0;
	int cnt; for(cnt=1;*str=='\n'?++cnt:*str;str++);
	return cnt;
}

void cutLine(char* str)
{
	if(!str || !*str)return;
	char* str2=str;	for(;*str2&&*(str2+1)&&*str2!='\n';str2++);	str2++;
	memmove(str,str2,strlen(str2)+1);
}

void drawFrame()
{
	u8* bufAdr=gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);

	memset(bufAdr, 0, 400*240*3);

	u32 ip = gethostid();
	char bof[256];
	sprintf(bof, "IP: %lu.%lu.%lu.%lu, port 5000, press B at any time to exit\n", ip & 0xFF, (ip>>8)&0xFF, (ip>>16)&0xFF, (ip>>24)&0xFF);
	gfxDrawText(GFX_TOP, GFX_LEFT, NULL, bof, 240-fontDefault.height*1, 10);

	int i = countLines(superStr);
	while(i>240/fontDefault.height-1){cutLine(superStr);i--;}
	gfxDrawText(GFX_TOP, GFX_LEFT, NULL, superStr, 240-fontDefault.height*2, 20);

	gfxFlushBuffers();
	gfxSwapBuffers();
}

void fsMkDir(char *dirName)
{
        int ret=FSUSER_CreateDirectory(NULL, sdmcArchive, FS_makePath(PATH_CHAR, dirName));
        if(ret == PATH_EXISTS){
//                print("\n directory exists %s", dirName);
        }else{
//                print("\n create directory result %s (%08X)", dirName, ret);
        }  
}

void unicodeToChar(char* dst, u16* src)
{
        if(!src || !dst)return;
        while(*src)*(dst++)=(*(src++))&0xFF;
        *dst=0x00;
}

void fsTryListOrDeleteDir(char *name)
{
	Handle dirHandle;
	FSUSER_OpenDirectory(NULL, &dirHandle, sdmcArchive, FS_makePath(PATH_CHAR, name));

	u32 entriesRead=0;
	do{
		u16 entryBuffer[512];
		char data[256];

		FSDIR_Read(dirHandle, &entriesRead, 1, (FS_dirent*)entryBuffer);
		if(!entriesRead)break;
                
		unicodeToChar(data, entryBuffer);
		print("    -");
		print(data);
		print("\n");
	}while(entriesRead>0);
	FSDIR_Close(dirHandle);

	FSUSER_DeleteDirectory(NULL, sdmcArchive, FS_makePath(PATH_CHAR, name));
}

void fsRmFileAndDir(char *name)
{
	fsTryListOrDeleteDir(name);
	FSUSER_DeleteFile(NULL, sdmcArchive, FS_makePath(PATH_CHAR, name));
}

void fsystemInit()
{
        Result ret;
        ret=fsInit();
        print("fsInit %08X\n", (unsigned int)ret);

        sdmcArchive=(FS_archive){0x00000009, (FS_path){PATH_EMPTY, 1, (u8*)""}};
        FSUSER_OpenArchive(NULL, &sdmcArchive);

        print("FSUSER_OpenArchive %08X\n", (unsigned int)ret);
}

void socketInit()   
{
        Result ret;
        ret=SOC_Initialize((u32*)memalign(0x1000, 0x100000), 0x100000);
        print("SOC_Initialize %08X\n", (unsigned int)ret);
}
        
void socketExit()
{
        SOC_Shutdown();
}

int openServer()
{
    struct sockaddr_in myaddr;
    int mysocket;
    mysocket = socket( AF_INET, SOCK_STREAM, 0 );
    print("Created Socket!\n");
    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(80);
    myaddr.sin_addr.s_addr= INADDR_ANY;

    bind(mysocket, (struct sockaddr *)&myaddr,sizeof(myaddr));
    listen(mysocket,1);

    return mysocket;
}

int acceptConnection(int serverSocket)
{
    struct sockaddr_in otheraddr;
    int socket;
    socklen_t len;

    len = sizeof(otheraddr);
    socket = accept(serverSocket, (struct sockaddr *)&otheraddr, &len);

    return socket;
}

#define BSZ (32*1024)
char buf[BSZ];
int recvFile(int socket)
{
    unsigned int len=0,i=0,sz=0;
    char b,n=0;

    while(1) {
        int rdlen = recv(socket, &b, 1, 0 );
        if(rdlen==-1) return 0;

        if( sz && (b=='/') && (buf[0]=='/') ){
            buf[sz]=0;
            fsMkDir(buf);
        }

        buf[sz]=b;
        if(!b) break;

        ++sz;
    }

    for(n=0;n<4;n++) {
        int rdlen = recv(socket, &b, 1, 0 );
        if(rdlen==-1) goto ERR;
        len += b<<(n*8);
    }

    if(len) {
        Handle fileHandle;
        int ret=FSUSER_OpenFile(NULL, &fileHandle, sdmcArchive, FS_makePath(PATH_CHAR, buf), FS_OPEN_WRITE|FS_OPEN_CREATE, 0);
        print("\n  storing %s (%08X)\n", buf, ret);
//      if(!f) {
//          print("erro criando arquivo %s\n",fPath);
//          return 0;
//      }

        print("receiving %s\n",buf);
        print("%d bytes\n",len);

        print("reading ");
        sz=0;
        while(i<len) {
            int rdlen = recv(socket, buf+sz, BSZ-sz, 0 );
            //print("rdlen %d\n",rdlen);
            print(".");
            if(rdlen<0) {
                FSFILE_Close(fileHandle);
                goto ERR;
            }

            sz+=rdlen;
            i+=rdlen;
            if( (sz==BSZ) || (i>=len) ) {
                FSFILE_Write(fileHandle, (u32*)&sz, i-sz, (u32*)buf, sz, 0x10001);

                if(i>=len) break;
                sz=0;
            }
        }
        FSFILE_Close(fileHandle);
        print("received!\n");
    } else {
        print("deleting %s\n",buf);
        fsRmFileAndDir(buf);
    }

    send(socket, "ok", 3, 0);
    return 1;

ERR:
    print("errorreceiving data!\n");
    return 0;
}

int main()
{
	int waitFrames=-1;
	int serverSocket=-1;
	int fileSocket=-1;

	srvInit();	
	aptInit();
	hidInit(NULL);
	gfxInit();

	gfxSet3D(false);

	superStr[0]=0;

	fsystemInit();
	socketInit();

	while(aptMainLoop())
	{
		drawFrame();

		if(waitFrames>=0) {
			if(waitFrames==0) {
				closesocket(fileSocket);
				closesocket(serverSocket);
			}
			--waitFrames;
		} else {
			hidScanInput();
			if(hidKeysDown()&KEY_B)break;

			if(hidKeysDown()&KEY_A) {
				serverSocket = openServer();
				fileSocket = acceptConnection(serverSocket);
				recvFile(fileSocket);
				waitFrames = 120;
			}
		}

		gspWaitForEvent(GSPEVENT_VBlank0, false);
	}

	socketExit();

	gfxExit();
	hidExit();
	aptExit();
	srvExit();
	return 0;
}
